import numpy as np
import sys,os,inspect
from PyQt4 import QtCore

from KModel.KSingleStock import KSingleStock
from KTrade.KStockFeed import KStockFeed
from KIndicators import KBaseIndicator
from PyQt4.QtGui import QGraphicsObject,QPen,QBrush,QColor,QPolygonF
from PyQt4 import QtCore,QtGui,Qt
from PyQt4.QtCore import QPointF
from KApp import KConstants as vm
class KAtomGartleyPattern(KBaseIndicator.KBaseIndicator):
	''' the atomic indicator to search for C Miner Patterns given peaks'''
	def __init__(self,parent,peakindicator): #need pass in peak indicator to work
		super(KAtomGartleyPattern,self).__init__(parent)
		self.peakindictor=peakindicator
	def GetPatternList(self):
		return self.patternlist
	def GetPredictList(self):
		if hasattr(self,'predictlist'):
			return self.predictlist
		else:
			return None

	def GetLogicRange(self):
		# get the range of all data in this indicator
		if self.predictlist:
			(pxrange,pyrange)=self.m_stock.CurWindowRange('Price')	
			minprice=None
			maxprice=None
			for pindex in range(0,len(self.predictlist)):
				lastpointX=self.patternlist[pindex][1][-2][0] # check the x of point B( in gartley) or  D ( in 3way)
				firstpointX=self.patternlist[pindex][1][0][0] # check the x of point X
				if firstpointX>=pxrange[0] and lastpointX<=pxrange[1]:
					curpreds=self.predictlist[pindex][0]
					#print(curpreds)
					if not maxprice or max(curpreds)>maxprice:
						maxprice=max(curpreds)
					if not minprice or min(curpreds)<=minprice:
						minprice=min(curpreds)
			if minprice and maxprice:
				return (pxrange,[minprice,maxprice])
		return self.m_stock.CurWindowRange('Price') # else return default



	def calculate(self):		
		KBaseIndicator.KBaseIndicator.update(self)
		narray=self.m_stock.GetNumpyArray()
		peaks=self.peakindictor.GetPeakList()
		windowsize=self.peakindictor.GetWindowSize()
		#self.patternlist=KAtomGartleyPattern.FindGartleyABCConsolidating(narray,peaks,windowsize)
		#self.predictlist=KAtomGartleyPattern.PredictGartleyTargetPrice(self.patternlist)
		#self.patternlist=KAtomGartleyPattern.Find3WayDriveTrend(narray,peaks,windowsize)
		#self.predictlist=KAtomGartleyPattern.Predict3WayDriveTargetPrice(self.patternlist)

		self.OnStart()
		self.stockfeed=KStockFeed(self.m_stock)
		stockfeed=self.stockfeed
		while stockfeed.HasNextBar():
			stockfeed.MoveForward()
			self.OnNewBars(stockfeed.GetCurrentIndex())		
		self.OnFinish()
		
		return self.patternlist	
	## online functions
	def OnNewBars(self,curfeedindex):
		# used in online mode, will be called when a new bar is available
		if self.peakindictor.GetPeakCount()>self.curpeakcount:
			# new peak is availbale
			curbars=self.stockfeed.GetBarsTillNow()
			windowsize=self.peakindictor.GetWindowSize()
			if self.peakindictor.GetPeakCount()>=3:
				peaklist=self.peakindictor.GetPeakList()
				LatestPeaks=peaklist[-4:]
				# find pattern
				pattern=KAtomGartleyPattern.FindGartleyABCConsolidatingForPeak(curbars,LatestPeaks,windowsize)
				if pattern:
					self.patternlist.append(pattern)
					# predict target price of pattern
					
		self.curpeakcount=self.peakindictor.GetPeakCount()
		
	def OnStart(self):
		# used in online mode, called before simulation started
		self.curpeakcount=0
		self.patternlist=[]
		
	def OnFinish(self):
		# used in online mode, called after simulation exectued
		print(len(self.patternlist))


	@staticmethod
	def FindGartleyABCConsolidatingForPeak(narray,peaklist,windowsize):
	# search for ABC gartley pattern among all extremas

		npworklist=np.array(peaklist)
		if abs(np.diff(npworklist[:,2])).min()<0.5: # skip if any neighbor extrema has same sign
			return 
		Bpoint=npworklist[-1,:]
		Apoint=npworklist[-2,:]
		Xpoint=npworklist[-3,:]
		Cpointleftlimit=Bpoint[0]+windowsize # offset of window size, C is not recognized yet until the windowsize passed.
		Cpointrightlimit=min([narray.shape[0],Bpoint[0]+Bpoint[0]-Xpoint[0]])
		if Bpoint[2]>0: #down trend consolidation
			if not Bpoint[1]<Xpoint[1]: # B should not exceed A
				return None
			# search for break points 
			breakpoint=None
			for sindex in range(int(Cpointleftlimit),int(Cpointrightlimit)):
				curpoint=narray[sindex,:]
				if curpoint[3]<Apoint[1]:
					breakpoint=[sindex,curpoint[3],-1] # found first points whole close price break through A point
					break
				elif curpoint[3]>Apoint[1]: # will invalidate the pattern
					return None

		else: # up trend consolidation
			if not Bpoint[1]>Xpoint[1]: # B should not exceed A
				return
			# search for break points 
			breakpoint=None
			for sindex in range(int(Cpointleftlimit),int(Cpointrightlimit)):
				curpoint=narray[sindex,:]
				if curpoint[3]>Apoint[1]:
					breakpoint=[sindex,curpoint[3],-1] # found first points whole close price break through A point
					break
				elif curpoint[3]<Apoint[1]: # will invalidate the pattern
					return None
		if breakpoint:
			return ['Gartley',[Xpoint,Apoint,Bpoint,breakpoint]] # recorgnize as valid pattern
		else:
			return None

	@staticmethod
	def PredictGartleyTargetPriceForPattern(each_pattern):
		# the problem is it is not easy to find 'last trend', see book <High Probability Trading>- P110
		Aratio=np.array([0.618,1.0,1.62])
		Bratio=np.array([1.27,1.62,2.62])
		SymbolTemplate=['A']*3+['B']*3
		
		predictlist=[]
		[Xpoint,Apoint,Bpoint,breakpoint]=each_pattern[1]
		Alength=Xpoint[1]-Apoint[1]
		Blength=Bpoint[1]-Apoint[1]
		ASamewayMappingSeg=Alength*Aratio
		Apredict=Bpoint[1]-ASamewayMappingSeg
		BRevserRetraceMappingSeg=Blength*Bratio
		Bpredict=Bpoint[1]-BRevserRetraceMappingSeg
		AllPredict=np.concatenate((Apredict,Bpredict),axis=0).tolist()
		result=[]
		predictlist.append([AllPredict,SymbolTemplate])


	@staticmethod
	def PredictGartleyTargetPrice(patternlist):
		# the problem is it is not easy to find 'last trend', see book <High Probability Trading>- P110
		Aratio=np.array([0.618,1.0,1.62])
		Bratio=np.array([1.27,1.62,2.62])
		SymbolTemplate=['A']*3+['B']*3
		predictlist=[]
		for each_pattern in patternlist:
			[Xpoint,Apoint,Bpoint,breakpoint]=each_pattern[1]
			Alength=Xpoint[1]-Apoint[1]
			Blength=Bpoint[1]-Apoint[1]
			ASamewayMappingSeg=Alength*Aratio
			Apredict=Bpoint[1]-ASamewayMappingSeg
			BRevserRetraceMappingSeg=Blength*Bratio
			Bpredict=Bpoint[1]-BRevserRetraceMappingSeg
			AllPredict=np.concatenate((Apredict,Bpredict),axis=0).tolist()
			result=[]
			predictlist.append([AllPredict,SymbolTemplate])

		return predictlist


	


	@staticmethod
	def Find3WayDriveTrend(narray,peaklist,windowsize):
	# search for ABC gartley pattern among all extremas

		npworklist=np.array(peaklist)


		patternlist=[]
		for index in range(4,len(peaklist)-1): # need at least 4 extrema to get start
			cursection=npworklist[index-4: index+1,:]
			if abs(np.diff(cursection[:,2])).min()<0.5: # skip if any neighbor extrema has same sign
				continue
			Dpoint=npworklist[index,:]		
			Cpoint=npworklist[index-1,:]
			Bpoint=npworklist[index-2,:]
			Apoint=npworklist[index-3,:]
			Xpoint=npworklist[index-4,:]

			Epointleftlimit=int(Dpoint[0]+windowsize) # offset of window size, C is not recognized yet until the windowsize passed.
			Epointrightlimit=int(min([narray.shape[0],Dpoint[0]+Dpoint[0]-Xpoint[0]]))
			if Dpoint[2]>0: #down trend 
				#print('%.2f %.2f %.2f' %(Xpoint[1],Bpoint[1],Dpoint[1]))
				if not Xpoint[1]>Bpoint[1]>Dpoint[1]: # X>B>D
					continue
				if not Apoint[1]>Cpoint[1]: # A>C
					continue
				Alength=abs(Xpoint[1]-Apoint[1])
				Clength=abs(Bpoint[1]-Cpoint[1])
				DWave=narray[Cpoint[0]:Dpoint[0],:]
				if DWave.shape[0]==0:
					continue			
				DWaveHigh=np.max(DWave[:,3])
				if DWaveHigh>Apoint[1]: # none of the bar's close price in wave D should fall in wave A
					continue
				# search for candidate of E points, which will be the end of wave E.
				breakpoint=None
				for sindex in range(Epointleftlimit,Epointrightlimit):
					curpoint=narray[sindex,:]
					ELength=abs(curpoint[3]-Dpoint[1])
					if not curpoint[3]<Cpoint[1]: # should lower than the end of wave C
						continue
					if Clength<min([ELength,Alength]): # C length should not be the shortest among A,C,E
						continue
					breakpoint=[sindex,curpoint[3],-1] # found first points which could be E point.
					break

			else: # up trend consolidation
				if not Xpoint[1]<Bpoint[1]<Dpoint[1]: # X<B<D
					continue
				if not Apoint[1]<Cpoint[1]: # A<C
					continue
				Alength=abs(Xpoint[1]-Apoint[1])
				Clength=abs(Bpoint[1]-Cpoint[1])
				DWave=narray[Cpoint[0]:Dpoint[0],:]
				if DWave.shape[0]==0:
					continue
				DWaveMin=np.min(DWave[:,3])
				if DWaveMin<Apoint[1]: # none of the bar in wave D should fall in wave A
					continue
				# search for candidate of E points, which will be the end of wave E.
				breakpoint=None
				for sindex in range(Epointleftlimit,Epointrightlimit):
					curpoint=narray[sindex,:]
					ELength=abs(curpoint[3]-Dpoint[1])
					if not curpoint[3]>Cpoint[1]: # should higher than the end of wave C
						continue
					if Clength<min([ELength,Alength]): # C length should not be the shortest among A,C,E
						continue
					breakpoint=[sindex,curpoint[3],-1] # found first points which could be E point.
					break
			if breakpoint:
				patternlist.append(['3Way',[Xpoint,Apoint,Bpoint,Cpoint,Dpoint,breakpoint]])

		return patternlist
	

	@staticmethod
	def Predict3WayDriveTargetPrice(patternlist):
		# see book <High Probability Trading>- P114
		
		Aratio=np.array([0.382,0.618])
		Bratio=np.array([1.00])
		Craito=np.array([1.27,1.62])
		SymbolTemplate=['A']*3+['B']*1+['C']*3	
		predictlist=[]
		for each_pattern in patternlist:
			[Xpoint,Apoint,Bpoint,Cpoint,Dpoint,breakpoint]=each_pattern[1]
			alength=Apoint[1]-Xpoint[1]
			blength=Apoint[1]-Bpoint[1]
			clength=Cpoint[1]-Bpoint[1]
			dlength=Cpoint[1]-Dpoint[1]
			# 1st map: same way mapping starting from point D, using ( max wave length from wave #1 to #3 ) as base segment
			MaxABClength=np.max(np.abs([alength,blength,clength]))
			Max3SamewayMappingSeg=MaxABClength*Aratio
			Apredict=Dpoint[1]+Max3SamewayMappingSeg if Dpoint[2]<0 else Dpoint[1]-Max3SamewayMappingSeg
			
			# 2nd map: same way mapping starting from point D, using length of wave #1 as base segment 			
			ASamewayMappingSeg=alength*Bratio
			Bpredict=Dpoint[1]+ASamewayMappingSeg	
			
			# 3rd map: revers mapping starting from point D, using length of wave #4 as base segment
			DReverseMappingSeg=dlength*Craito
			Cpredict=Dpoint[1]+DReverseMappingSeg
			
			# merge
			AllPredict=np.concatenate((Apredict,Bpredict,Cpredict),axis=0).tolist()
			result=[]
			print(AllPredict)
			predictlist.append([AllPredict,SymbolTemplate])

		return predictlist
class KAtomGartleyPatternDisplayItem(KBaseIndicator.KBaseIndicatorDisplayItem):

	def PaintPattern(self,painter):
		if not hasattr(self,'model') :
			return
		if not self.model.IsReady():
			return 

		if self.model.IsResultExpired()==True:
			self.model.calcluate()

		# start painting		

		linepen=QPen(QColor(0,98,195))
		linepen.setStyle(QtCore.Qt.DashLine)
		linepen.setWidthF(2)

		textpen=QPen(QColor(255,0,0))
		textpen.setStyle(QtCore.Qt.DashLine)
		textpen.setWidthF(1)
		painter.setPen(linepen)

		painter.setFont(self.GetFont('labelText'))

		width=1.0/2.0
		#(test,())=self.viewmodel.mapLogiceSizeToScene((width,))
		[width],[]=self.viewmodel.mapLogiceSizeToScene(vm.RectType.ContentBound,[width])

		# only show extrema in current window
		XCordinates=self.m_stock.CurWindowData('Index')
		minx=min(XCordinates)
		maxx=max(XCordinates)
		patternlist=self.model.GetPatternList()
		predlist=self.model.GetPredictList()
		patternwindow=[]

		predwindow=[]
		for patternindex in range(0,len(patternlist)):
			each_pattern=patternlist[patternindex]
			if each_pattern[1][0][0]>minx and each_pattern[1][-1][0]<maxx:
				patternwindow.append(each_pattern)
				if predlist:
					predwindow.append(predlist[patternindex])

		patternwindow=[x[1] for x in patternlist if x[1][0][0]>minx and x[1][-1][0]<maxx]

		textlist=['X','A','B','C','D','E']
		for index in range(0,len(patternwindow)):

			# draw pattern
			npwindow=np.array(patternwindow[index])
			xcords=npwindow[:,0].tolist()
			ycords=npwindow[:,1].tolist()
			(xcords,ycords)=self.viewmodel.mapLogiceCordToScene(vm.RectType.ContentBound,xcords,ycords)
			for pindex in range(0,len(xcords)-1):
				startp=QPointF(xcords[pindex],ycords[pindex])
				endp=QPointF(xcords[pindex+1],ycords[pindex+1])
				textp=QPointF(xcords[pindex]+2*width,ycords[pindex])
				painter.setPen(linepen)
				painter.drawLine(startp,endp)	
				painter.setPen(textpen)
				painter.drawText(textp,textlist[pindex]) # print label
			pindex+=1
			textp=QPointF(xcords[pindex]+2*width,ycords[pindex])
			painter.drawText(textp,textlist[pindex]) # print potential pattern end points
			# draw prediction
			if predwindow:
				painter.setPen(linepen)
				predpoints=predwindow[index]
				([],predycords)=self.viewmodel.mapLogiceCordToScene(vm.RectType.ContentBound,[],predpoints[0])
				LastIndex=len(xcords)-2
				for index in range(0,len(predpoints[0])):
					if predpoints[1][index]=='A':
						linewidth=width
					else:
						linewidth=width*0.7
					startp=QPointF(xcords[LastIndex],predycords[index])
					endp=QPointF(xcords[LastIndex]+linewidth,predycords[index])
					painter.drawLine(startp,endp)



					@staticmethod
					def FindGartleyABCConsolidating(narray,peaklist,windowsize):
					# search for ABC gartley pattern among all extremas
				
						npworklist=np.array(peaklist)
				
				
						patternlist=[]
						for index in range(2,len(peaklist)-1): # need at least 3 extrema to get start
							cursection=npworklist[index-2: index+1,:]
							if abs(np.diff(cursection[:,2])).min()<0.5: # skip if any neighbor extrema has same sign
								continue
							Bpoint=npworklist[index,:]
							Apoint=npworklist[index-1,:]
							Xpoint=npworklist[index-2,:]
							Cpointleftlimit=Bpoint[0]+windowsize # offset of window size, C is not recognized yet until the windowsize passed.
							Cpointrightlimit=min([narray.shape[0],Bpoint[0]+Bpoint[0]-Xpoint[0]])
							if Bpoint[2]>0: #down trend consolidation
								if not Bpoint[1]<Xpoint[1]: # B should not exceed A
									continue
								# search for break points 
								breakpoint=None
								for sindex in range(int(Cpointleftlimit),int(Cpointrightlimit)):
									curpoint=narray[sindex,:]
									if curpoint[3]<Apoint[1]:
										breakpoint=[sindex,curpoint[3],-1] # found first points whole close price break through A point
										break
									elif curpoint[3]>Apoint[1]: # will invalidate the pattern
										continue
				
							else: # up trend consolidation
								if not Bpoint[1]>Xpoint[1]: # B should not exceed A
									continue
								# search for break points 
								breakpoint=None
								for sindex in range(int(Cpointleftlimit),int(Cpointrightlimit)):
									curpoint=narray[sindex,:]
									if curpoint[3]>Apoint[1]:
										breakpoint=[sindex,curpoint[3],-1] # found first points whole close price break through A point
										break
									elif curpoint[3]<Apoint[1]: # will invalidate the pattern
										continue
							if breakpoint:
								patternlist.append(['Gartley',[Xpoint,Apoint,Bpoint,breakpoint]])
				
				
						return patternlist
						
					
	def PrepareRenderPicture(self):
		## pre-computing a QPicture object allows paint() to run much more quickly, 
		## rather than re-drawing the shapes every time.

		try:
			if not hasattr(self,'model') :
				return
			if not self.model.IsReady():
				return 		
			if self.model.IsResultExpired()==True:
				self.model.calculate()	

			self.picture = QtGui.QPicture()
			painter = QtGui.QPainter(self.picture)		
			self.PaintPattern(painter)
		except:
			print('faild to render CMinderPattern')

		painter.end()	